package com.shallowUniverse.process.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shallowUniverse.process.context.user.ProcessUserService;
import com.shallowUniverse.process.context.user.SysUser;
import com.shallowUniverse.process.dto.AppointParam;
import com.shallowUniverse.process.dto.TableField;
import com.shallowUniverse.process.dto.TableFieldCondition;
import com.shallowUniverse.process.entity.Approval;
import com.shallowUniverse.process.entity.ProcessDefinition;
import com.shallowUniverse.process.entity.ProcessNodeDefinition;
import com.shallowUniverse.process.exception.ApprovalNotExistException;
import com.shallowUniverse.process.exception.ProcessNodeDefinitionErrorException;
import com.shallowUniverse.process.exception.ProcessNotCompletedException;
import com.shallowUniverse.process.exception.ProcessNotFoundException;
import com.shallowUniverse.process.mapper.ApprovalDetailsMapper;
import com.shallowUniverse.process.mapper.ApprovalMapper;
import com.shallowUniverse.process.service.ProcessDefinitionService;
import com.shallowUniverse.process.service.ProcessNodeDefinitionService;
import com.shallowUniverse.process.service.ApprovalService;
import com.shallowUniverse.process.utils.AssertUtil;
import com.shallowUniverse.process.utils.Builder;
import com.shallowUniverse.process.utils.Constant;
import com.shallowUniverse.process.utils.ProcessUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author gs
 * @date create in 2020/12/11 16:34
 */
@AllArgsConstructor
@Slf4j
public class ApprovalServiceImpl implements ApprovalService {

    private final ApprovalMapper approvalMapper;
    private final ProcessUserService processUserService;
    private final ApprovalDetailsMapper approvalDetailsMapper;
    private final ProcessDefinitionService processDefinitionService;
    private final ProcessNodeDefinitionService processNodeDefinitionService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int save(String tableName, Map<Constant, Object> params) throws ProcessNodeDefinitionErrorException, ProcessNotFoundException {
        //创建approval
        Approval approval = new Approval();
        //设置提交人
        approval.setUserId(processUserService.getUserId());
        //设置当前日期
        approval.setCreateDate(DateUtil.now());

        //拿到流程号和主键
        Object primaryKey = params.get(Constant.PRIMARY_KEY);
        log.info("目标主键为：" + primaryKey);
        //拿到流程id字段值
        Object processField = params.get(Constant.PROCESS_FIELD);
        //通过流程号拿到流程
        ProcessDefinition processDefinition = processDefinitionService.findById(Long.parseLong(processField.toString()));
        //验证是否还存在该流程
        AssertUtil.processDefinitionNotFound(processDefinition);
        log.info("拿到流程为：" + processDefinition.getName());
        //通过流程号拿到第一个节点
        List<ProcessNodeDefinition> processNodeDefinitionList = processNodeDefinitionService.findFirstNode(processDefinition.getId());
        //判断第一个节点是否为空
        AssertUtil.isProcessNodeDefinitionError(processNodeDefinitionList);

        //设置流程id
        approval.setProcessId(processDefinition.getId());
        //先根据角色和部门查询当前登录用户是否存在审批角色
        Map<String, String> roleParams = new HashMap<>(2);
        roleParams.put("processId", processDefinition.getId().toString());
        roleParams.put("processNodeRole", ProcessUtil.parseCollectionToString(processUserService.getUserRoles()));
        roleParams.put("deptId", ObjectUtils.nullSafeToString(processUserService.get().getUserDept()));
        List<ProcessNodeDefinition> maxNodeByRoleList = processNodeDefinitionService.findMaxNodeByRole(roleParams);
        //判断是否为空（不为空证明存在，重新赋值）
        if (!CollectionUtils.isEmpty(maxNodeByRoleList)) {
            //先拿到最大的值
            ProcessNodeDefinition maxProcessNodeDefinition = maxNodeByRoleList.get(0);
            //过滤出来所有最大的顺序并重新复制给当前节点
            processNodeDefinitionList = maxNodeByRoleList.stream().filter(
                    processNodeDefinition -> processNodeDefinition.getSeq().equals(maxProcessNodeDefinition.getSeq())
            ).collect(Collectors.toList());
        }
        //查找下一个节点
        List<ProcessNodeDefinition> nextNodeList = processNodeDefinitionService.renderNextNode(processNodeDefinitionList.get(0));
        if (CollectionUtils.isEmpty(nextNodeList)) {
            //判断角色是否包含在当前流程节点中，包含并且不存在下一个节点直接审批通过
            if (!CollectionUtils.isEmpty(maxNodeByRoleList)) {
                approval.setStatus(Constant.PROCESS_ALLOW_SIGN);
            }
            //设置当前节点
            approval.setCurrent(ProcessUtil.joinProcessNodeDefinition(processNodeDefinitionList));
        } else {
            //判断是否包含在审批节点列表中则跳到下一个节点
            if (!CollectionUtils.isEmpty(maxNodeByRoleList)) {
                approval.setCurrent(ProcessUtil.joinProcessNodeDefinition(nextNodeList));
            } else {
                //不为0并且不包含在审批节点列表中则保持当前节点
                approval.setCurrent(ProcessUtil.joinProcessNodeDefinition(processNodeDefinitionList));
            }
        }

        //判断是否已经设置了状态
        if (ObjectUtils.isEmpty(approval.getStatus())) {
            approval.setStatus(Constant.PROCESS_HANDLE_SIGN);
        }

        log.debug("流程第一个节点为：" + ProcessUtil.parseCollectionToString(processNodeDefinitionList.stream().map(ProcessNodeDefinition::getName).collect(Collectors.toList())));

        //设置目标表id
        approval.setTargetId(primaryKey.toString());
        //设置目标表名
        approval.setTarget(tableName);
        //设置目标表主键字段名
        //判断是否是驼峰
        boolean underScoreCamelCase = (boolean) params.get(Constant.UNDERSCORE_TO_CAMEL_CASE);
        if (underScoreCamelCase) {
            approval.setPrimaryKey(params.get(Constant.PRIMARY_KEY_FIELD).toString().replaceAll("[A-Z]", "_$0").toLowerCase());
        } else {
            approval.setPrimaryKey(params.get(Constant.PRIMARY_KEY_FIELD).toString());
        }

        //保存approval
        return approvalMapper.save(approval);
    }

    @Override
    public Map<String, Object> findTableData(Long id) throws ApprovalNotExistException {
        Approval approval = approvalMapper.selectById(id);
        //判断是否为空
        AssertUtil.approvalNotNull(approval);
        TableFieldCondition tableFieldCondition = new TableFieldCondition();
        tableFieldCondition.setColumn(approval.getPrimaryKey());
        tableFieldCondition.setId(approval.getTargetId());
        tableFieldCondition.setTable(approval.getTarget());
        return approvalMapper.selectTableData(tableFieldCondition);
    }

    @Override
    public List<Approval> findByRole(Approval approval) {
        SysUser sysUser = processUserService.get();
        List<Approval> approvals = approvalMapper.selectByRole(ProcessUtil.parseCollectionToString(sysUser.getUserRoles()), sysUser.getUserId(), sysUser.getUserDept(), approval);
        //设置当前节点名称和目标表数据
        ProcessUtil.populateProcessNodeAndTarget(approvals, processNodeDefinitionService, this);
        //返回
        return approvals;
    }

    @Override
    public List<Approval> findByUser(Approval approval) {
        List<Approval> approvals = approvalMapper.selectByUser(processUserService.getUserId(), approval);
        //设置当前节点名称和目标表数据
        ProcessUtil.populateProcessNodeAndTarget(approvals, processNodeDefinitionService, this);
        //返回
        return approvals;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int update(Approval approval) {
        return approvalMapper.update(approval);
    }

    @Override
    public List<TableField> findTableFields(Long id) throws ApprovalNotExistException {
        Approval approval = approvalMapper.selectById(id);
        //判断是否为空
        AssertUtil.approvalNotNull(approval);
        return approvalMapper.selectTableFields(approval.getTarget());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int remove(List<Long> ids, boolean isLogic) throws ProcessNotCompletedException {
        //判断是否传了id
        if (CollectionUtils.isEmpty(ids)) {
            return 0;
        }
        //转换id
        String idsStr = ProcessUtil.parseCollectionToString(ids);
        //判断是否存在未完成的审批单
        AssertUtil.processNotCompleted(approvalMapper.selectNotCompletedByIds(idsStr));
        //判断是逻辑删除还是直接删除
        if (isLogic) {
            return approvalDetailsMapper.deleteByApprovalLogic(idsStr) + approvalMapper.deleteLogic(idsStr);
        } else {
            return approvalDetailsMapper.deleteByApproval(idsStr) + approvalMapper.delete(idsStr);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateApprovePerson(AppointParam appointParam) {
        //看是否传递了参数
        if (ObjectUtils.isEmpty(appointParam) || CollectionUtils.isEmpty(appointParam.getApprovalIds()) || CollectionUtils.isEmpty(appointParam.getAppointIds())) {
            return 0;
        }
        //封装参数
        Map<String, String> params = new HashMap<>(2);
        params.put("approvalId", ProcessUtil.parseCollectionToString(appointParam.getApprovalIds()));
        params.put("appoint", ProcessUtil.parseCollectionToString(appointParam.getAppointIds()));
        return approvalMapper.updateApprovePerson(params);
    }

    @Override
    public IPage<Approval> findPageByRole(IPage<Approval> page, Approval approval) {
        SysUser sysUser = processUserService.get();
        IPage<Approval> approvalIPage = approvalMapper.selectPageByRole(page, ProcessUtil.parseCollectionToString(sysUser.getUserRoles()), sysUser.getUserId(), sysUser.getUserDept(), approval);
        //设置当前节点名称和目标表数据
        ProcessUtil.populateProcessNodeAndTarget(approvalIPage.getRecords(), processNodeDefinitionService, this);
        //返回
        return approvalIPage;
    }

    @Override
    public IPage<Approval> findPageByUser(IPage<Approval> page, Approval approval) {
        IPage<Approval> approvalIPage = approvalMapper.selectPageByUser(page, processUserService.getUserId(), approval);
        //设置当前节点名称和目标表数据
        ProcessUtil.populateProcessNodeAndTarget(approvalIPage.getRecords(), processNodeDefinitionService, this);
        //返回
        return approvalIPage;
    }

    @Override
    public List<Approval> find(Approval approval) {
        List<Approval> approvals = approvalMapper.selectList(approval);
        //设置当前节点名称和目标表数据
        ProcessUtil.populateProcessNodeAndTarget(approvals, processNodeDefinitionService, this);
        //返回
        return approvals;
    }

    @Override
    public IPage<Approval> findPage(IPage<Approval> page, Approval approval) {
        IPage<Approval> approvalIPage = approvalMapper.selectPageList(page, approval);
        //设置当前节点名称和目标表数据
        ProcessUtil.populateProcessNodeAndTarget(approvalIPage.getRecords(), processNodeDefinitionService, this);
        //返回
        return approvalIPage;
    }

    @Override
    public Approval findById(Long id) {
        Approval approval = approvalMapper.selectById(id);
        //封装目标数据和节点
        ProcessUtil.populateProcessNodeAndTarget(
                Builder.of(ArrayList<Approval>::new)
                        .with(List::add, approval)
                        .build(),
                processNodeDefinitionService, this
        );
        return approval;
    }
}
